package com.oros.db;

import java.util.LinkedHashMap;
import java.util.HashSet;

import com.vaadin.data.Container;
import com.vditl.core.UniVector;

public class AEViewContainer extends AETableContainer {

	private static final long serialVersionUID = 4106847914890977883L;
	final public static int VIEW_INNER_JOIN = 0;
	final public static int VIEW_LEFT_OUTER_JOIN=1;
	public int containerNo,sourceNo;
	public int[] columnAmounts,basePos;
	public boolean hasGeneratedColumns;
	public AETableContainer[] tables;
	public int[] joinColumns,idColumnPos;
	public int[] leftOuterJoin;
	private Container.ItemSetChangeListener containerListeners; 
	private boolean restrictRefreshInternal = false;
	private LinkedHashMap<String,Integer> joinColumnsMap;
	private boolean[] readOnlyTables;
	private Object[] defaultObjectIds;
	public AEViewContainer(AEViewDefinition definition) throws Exception
    {
    	super(definition,false,null);
    	containerNo = definition.containerNo;
    	readOnlyTables = definition.readOnlyTables;
    	sourceNo = definition.sourceNo;
    	columnAmounts = definition.columnAmounts;
    	hasGeneratedColumns = definition.hasGeneratedColumns;
    	tables = definition.tables;
    	joinColumns = definition.joinColumns;
    	leftOuterJoin = definition.leftOuterJoin;
    	generatedData = definition.generatedData;
        defaultObjectIds = new Object[containerNo];
        basePos = new int[containerNo];
        idColumnPos = new int[containerNo];
        int basis=0;
        for(int i=0;i<containerNo;i++)
        {
        	defaultObjectIds[i] = readOnlyTables[i]?new Integer(0):null;
        	basePos[i]=basis;
        	idColumnPos[i]=basis+tables[i].idColumn;
        	basis+=columnAmounts[i];
        }
    	addContainerFilter_NoRefresh(definition.whereFilters);
     	if (definition.sortColumns!=null)
    	{
    		sort_noRefresh(definition.sortColumns,definition.ascending);
    	}
        joinColumnsMap = new LinkedHashMap<String,Integer>(joinColumns.length * 2);
        for(int i=0;i<joinColumns.length;i++)
        {
        	joinColumnsMap.put(columnNames[joinColumns[i]],new Integer(i));
        	joinColumnsMap.put(columnNames[tables[i+1].idColumn+basePos[i+1]],new Integer(i));
        }

    	containerListeners = new Container.ItemSetChangeListener(){
			private static final long serialVersionUID = 1L;

			@Override
			public void containerItemSetChange(
					com.vaadin.data.Container.ItemSetChangeEvent event) {
				try
				{
				subcontainersRefreshEvent();
				} catch(Exception e)
				{
					//this is to be developed!!!
				}
			}
    		
    	};
    	for(int i=0;i<containerNo;i++)
    	{
    		tables[i].addListener(containerListeners);
    	}
        //refresh();    	
    }
	public boolean removeItem(Object itemId,int containerLevel)
			throws UnsupportedOperationException {
			for(int i=0;i<=containerLevel;i++)
			{
				if (readOnlyTables[i]) return false;
			}
			if (!sourceReady) makeSourceReady();
		AERawItem item =(AERawItem)source.get(itemId);
		if (item==null) return false;
        if (containerLevel>0)
        {
    		int idcolno = idColumnPos[containerLevel];
            String idcolname = columnNames[idcolno];
            Object id = item.getColumnValue(idcolno);
            String baseidcolname=columnNames[idColumn];
            
        	HashSet<?> hset=getDistinctColumnValuesUnfiltered(baseidcolname,idcolname,id);
        	
        	for(Object iteId:hset)
        	{
        		
        		AERawItem ite =(AERawItem) source.get(iteId);
        		
        		for(int i=0;i<=containerLevel;i++)
        		{
        			Object subide = ite.getColumnValue(idColumnPos[i]);
        			if (subide!=null) tables[i].removeItemDirect(subide);
        		}
        		source.remove(ite);
        		delsource.add(ite);
        	}
        }
        else
        {
		  source.remove(item);
		  delsource.add(item);
        }
		setModified(true);
		return true;
	}
	
	@Override
	public boolean removeItem(Object itemId)
			throws UnsupportedOperationException {
        return removeItem(itemId,0);
	}
	final private void subcontainersRefreshEvent() throws Exception
	{

		   refreshInternal();
	}
    public AEViewContainer(String viewName,AETableContainer[] tables,boolean[] readOnlyTables,String[] joinLeftTableFields,
    		int[] leftOuterJoin,Filter[] whereFilters,String[] sortColumns,boolean[] ascending,
    		LinkedHashMap<String,String> ColumnNameAliases,AEGeneratedColumn[] generatedColumns,
    		AEGroupInfo[] groupBy) throws Exception
    {
    	this(new AEViewDefinition(viewName,tables,readOnlyTables,joinLeftTableFields,leftOuterJoin,
    			whereFilters,sortColumns,ascending,ColumnNameAliases,generatedColumns,groupBy));
    }
    @Override
    public Object addItem()  throws UnsupportedOperationException 
    {
    	return addItem(null,null,null);
    }
    private boolean copyValuesOfSubcontainer(Object[] values,int container,Object id)
    {
    	AERawItem item = (AERawItem)tables[container].getItem(id);
    	if (item==null) return false;
    	int basis=basePos[container];
    	int colamnt = columnAmounts[container];
    	for(int i=0;i<colamnt;i++)
    	{
    		values[basis+i]=item.getColumnValue(i);
    	}
    	if (container>0)
    	{
    		if (id instanceof AERawItem) id = ((AERawItem)id).getPrimaryColumnValue();
    		values[joinColumns[container-1]]=id;
    	}
    	return true;
    }
    private boolean setValuesOfSubcontainer(Object itemId,int container,Object id)
    {
    	AERawItem item = (AERawItem)tables[container].getItem(id);
    	if (item==null) return false;
    	AERawItem dstItem = (AERawItem)getItem(itemId);
    	if (dstItem==null) return false;
    	int basis=basePos[container];
    	int colamnt = columnAmounts[container];
    	for(int i=0;i<colamnt;i++)
    	{
    		dstItem.setPropertyOfItem(basis+i, item.getColumnValue(i));
    	}
    	if (container>0)
    	{
    		if (id instanceof AERawItem) id = ((AERawItem)id).getPrimaryColumnValue();
    		dstItem.setPropertyOfItem(joinColumns[container-1],id);
    	}
    	return true;
    }
    public Object addItem(Object[] tableIds,String[] columns,Object[] values)
    {	
    	int n = columns==null?0:columns.length;
    	int k = values==null?0:values.length;
    	if (k!=n) throw new RuntimeException("AEViewContainer.addItem parameter values must be of the same dimension as columns");
    	if (tableIds==null) tableIds = defaultObjectIds.clone();
    	else tableIds = tableIds.clone();
    	if (tableIds.length!=containerNo) throw new RuntimeException("AEViewContainer.addItem parameter tableIds must have the dimension equal to the number of tables");
    	Object[] data = new Object[columnAmount];
    	int basis=0;
        for(int i=0;i<containerNo;i++)
        {
        	int nn=columnAmounts[i];
        	for(int j=0;j<nn;j++)
        	{
        		columnDefaults[basis+j]=tables[i].columnDefaults[j];
        	}
        	if (i>0 && !readOnlyTables[i] && tableIds[i]!=null && (tableIds[i].equals(new Integer(0)) ||
        			tableIds[i].equals(new Long(0L))))
        	{
        		long code = joinColumns[i-1]; 
        		Object val = multiEqualMap.getValueByKey(code);
        		if (val!=null) tableIds[i]=val;
        		else
        		{
        			code = basis + tables[i].getPrimaryColumnNo();
        			val = multiEqualMap.getValueByKey(code);
            		if (val!=null) tableIds[i]=val;		
        		}
        	}
        	basis+=nn;
        }
    	for(int i=0;i<columnAmount;i++)
    	{
    		data[i]=columnDefaults[i];
    	}
        for(int i=0;i<containerNo;i++) 
        {
        	if (tableIds[i]==null)
        	{
        	  if (!readOnlyTables[i])
        	  {
        	    Object id = tables[i].addItem();
        	      if (id==null)
        	      {
        		     try
        		     {
        		        rollback();
        		     } catch(Exception e)
        		     {
        			  }
        		     return null;
        	      } 
        	      copyValuesOfSubcontainer(data,i,id);
        	  }
        	}
        	else
        	{
        		Object id=tableIds[i];
        		if (!copyValuesOfSubcontainer(data,i,id) && !readOnlyTables[i])
        		{
        			long idcode=0; 
                    if (id instanceof Integer) idcode =((Integer)id).longValue();
                    else if (id instanceof Long) idcode =((Long)id).longValue();
                    if (idcode>0)
                    {
                    	Object Objectid = tables[i].addItem(id);
                    	if (Objectid==null)
                    	{
                    		try { rollback();}catch(Exception e){};
                    		return null;
                    	}
                    	copyValuesOfSubcontainer(data,i,id);
                    }
        		}
        	}
        }
    	for(int i=0;i<n;i++)
    	{
    		int colno = getColumnIndex(columns[i]);
    		if (colno<0) throw new RuntimeException("AEViewContainer.addItem column "+columns[i]+" is not present in the container");
    		Object v = values[i];
    		if (v instanceof AERawItem) v = ((AERawItem)v).getPrimaryColumnValue();
            data[colno]=v;
    	}
    	fillDataWithEqualContainerFilterValues(data);
		AERawItem item = new AERawItem(this,columnNames,data,columnTypes,true,primaryKey,generatedData);
		if (!sourceReady) makeSourceReady();
		source.add(item);
		editSource.add(item);
 		registerItemProperties(item);
 		setModified(true);
   	    return item.getPrimaryColumnValue();
	}
    
    @Override
    public void rollback() throws Exception
    {
    	for(int i=0;i<containerNo;i++)
    		if (!readOnlyTables[i]) tables[i].rollback();
    	delsource.clear();
    	editSource.clear();
    	restrictRefreshInternal=false;
    	modified = false;
    	refreshInternal();
    }
    private boolean refreshInternal()
    {
    	if (restrictRefreshInternal) return false;
    	this.restrictEventFire = true;
    	if (source==null) source = new UniVector(); else source.clear();
    	sourceReady = true;
    	int n = tables[0].size();
    	Object[] data=new Object[columnAmount]; 
    	for(int i=0;i<n;i++)
    	{
    		AERawItem main =(AERawItem)tables[0].getIdByIndex(i);
    		int m=0,basis=0;boolean filter=true;
    		while (m<containerNo)
    		{
                int p=columnAmounts[m];
                if (main!=null)
                {
                   for(int j=0;j<p;j++)
                   {
                	 data[basis+j]=main.getColumnValue(j);
                   }
                }
                else
                {
                	for(int j=0;j<p;j++)
                		data[basis+j]=columnDefaults[basis+j];
                }
                basis+=p;
    			int mm=m++;
    			if (m<containerNo)
    			{
    				boolean isLeftOuter =leftOuterJoin[mm]==VIEW_LEFT_OUTER_JOIN;
    				Object nxtKey = data[joinColumns[mm]];
    				if (nxtKey==null) main=null;
    				else main=(AERawItem)tables[m].getItem(nxtKey);
                    if (main==null && !isLeftOuter)
                    {
                    	filter=false;break;
                    }
    			}
    		}
    		if (!filter) continue;
    		if (hasGeneratedColumns)
    		{
    			int u=columnAmounts[containerNo];
    			for(int j=0;j<u;j++)
    			{
    				data[basis+j]=columnDefaults[basis+j];
    			}
    		}
    		AERawItem item = new AERawItem(this,columnNames,data,columnTypes,false,primaryKey,generatedData);
    		if (delsource.contains(item)) continue;
    		if (editSource.contains(item))
    		{
    			item =(AERawItem) editSource.get(item);
    		}
            source.add(item);
     		registerItemProperties(item);
    	}
    	int ned = editSource.size();
    	for(int i=0;i<ned;i++)
    	{
    		AERawItem item =(AERawItem) editSource.get(i);
    		if (!source.contains(item))
    		{
    			source.add(item);
    			registerItemProperties(item);
    		}
    	}
    	resortFilterCheck();
    	this.restrictEventFire = false;
    	fireContentsChange();
    	return true;
    }
    public Object getIdAtLevel(Object itemId,int containerLevel)
    {
    	if (containerLevel<0 || containerLevel>containerNo) return null;
        int colno = basePos[containerLevel]+tables[containerLevel].idColumn;
        if (!sourceReady) makeSourceReady();
        AERawItem item =(AERawItem)source.get(itemId);
        return item.getColumnValue(colno);
    }
	@Override
	public boolean refresh() throws Exception
	{
		boolean oldModified = modified;
		restrictRefreshInternal = true;
		this.restrictEventFire = true;
		//set to false in refreshInternal
		try
		{
			
			for(int i=0;i<containerNo;i++)
				tables[i].refresh();
		}
		finally
		{
			restrictRefreshInternal = false;
		}
	    boolean res= refreshInternal();
	    modified = oldModified;
	    return res;
	}
	@Override
 	public void validate() throws Exception
 	{
		for(int i=0;i<containerNo;i++)
		{
			tables[i].validate();
		}
 	}
	protected void preCommitValidated() throws Exception
	{
		super.validate();
		int n = delsource.size();
		if (n>0)
		{
			for(int i=0;i<n;i++) 
				tables[0].removeItem(delsource.get(i));
			delsource.clear();
		}
		n = editSource.size();
		for(int i=0;i<n;i++)
		{
			AERawItem item =(AERawItem)editSource.get(i);
			if (item.isModified())
			{
				if (!isValidItem(item)) throw new AEInvalidItemException("Invalid item: "+item);
				for(int j=0;j<containerNo;j++) if (!readOnlyTables[j])
				{
					int idcolno = tables[j].idColumn;
					int basis = basePos[j];
					Object id = item.getColumnValue(idcolno+basis);
					AERawItem dstId =(AERawItem) tables[j].getItem(id);
					int amnt=columnAmounts[j];
					for(int k=0;k<amnt;k++) if (k!=idcolno)
					{
						Object val = item.getColumnValue(basis+k);
						dstId.setPropertyOfItem(k,val);
					}
				}
			}
		}
		editSource.clear();
	}
	@Override
	public void commitTransaction(AETransactionData transdata) throws Exception
	{
		int cont=containerNo;
		while ((cont--)>0) if (!readOnlyTables[cont])
		{
           tables[cont].commitTransaction(transdata);
		}
		modified = false;
		restrictRefreshInternal=false;
		
         refreshInternal();
         //showSourceDetailedInfo(SHOW_INFO_ALL);
         fireContentsChange();
	} 
	@Override
	public boolean setPropertiesOfItem(Object itemId,String columnName,Object value)
	{
		if (!super.setPropertiesOfItem(itemId, columnName, value)) return false;
		Integer joinno =joinColumnsMap.get(columnName);
        if (joinno!=null)
        {
        	int joinnr = joinno.intValue();
        	setValuesOfSubcontainer(itemId,joinnr+1,value);
        }
		return true;
	}
	@Override
	public void close()
	{
        super.close();
        if (containerListeners!=null)
        {
          for(int i=0;i<containerNo;i++)
        	tables[i].removeListener(containerListeners);
          containerListeners = null;
        }
	}
	@Override
	public void showSourceDetailedInfo(int options)
	{
		super.showSourceDetailedInfo(options);
		if ((options & SHOW_INFO_SUBTABLES)!=0)
		{
			for(int i=0;i<containerNo;i++)
			{
				System.out.println("Basic container "+i);
				tables[i].showSourceDetailedInfo(options);
			}
		}
	}
}
